#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/algorithm/minmax_element.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/range/join.hpp>
#include <boost/range/algorithm.hpp>
#include <vector>
#include <array>
#include <map>
#include <unordered_map>
#include <string>
#include "../message_circuit/lambda_to_function1.cpp"


//using namespace std;
using namespace boost;
//using boost::adaptors::transformed;
using namespace boost::adaptors;

template<typename R> //传入的是一个iterator_range
class LinqCpp {
    typedef typename R::value_type value_type;

public:
    LinqCpp(R range) : m_linqrange(range) {
    }

    auto begin() -> decltype((std::declval<R>()).begin()) {
        return m_linqrange.begin();
    }

    auto end() -> decltype((std::declval<R>()).end()) {
        return m_linqrange.end();
    }

    //选择操作
    template<typename F>
    //auto
    void
    select(F &&f) {
        //-> LinqCpp<transformed_range<typename function_traits<F>::function, R> > {
        auto fun = to_function(f);
        //return LinqCpp<transformed_range<typename function_traits<F>::function, R>>
        //        (boost::adaptors::transform(m_linqrange, fun));
    }

    //过滤操作
    template<typename F>
    auto
    //void
    where(const F &&f)
    -> LinqCpp<boost::filtered_range<F, R>> {
        return LinqCpp<filtered_range<F, R>>
                (filter(m_linqrange, f));
    }

    //template<typename F>
    // auto max(const F &f) const -> value_type
    auto max() -> value_type {
        return *max_element(begin(), end());
    }

    template<typename Fn>
    unordered_multimap<typename std::result_of<Fn(value_type)>::type, value_type>
    groupby(const Fn &&f) {
        typedef decltype(std::declval<Fn>()(std::declval<value_type>())) keytype;
        unordered_multimap<keytype, value_type> mymap;
        std::for_each(begin(), end(), [&mymap, &f](value_type item) {
            mymap.insert(make_pair(f(item), item));
        });
        return mymap;
    }

    template<typename KeyFn, typename ValueFn>
    unordered_multimap<typename std::result_of<KeyFn(value_type)>::type,
            typename std::result_of<ValueFn(value_type)>::type>
    groupby(const KeyFn &&fnk, const ValueFn &&fnv) {
        typedef typename std::result_of<KeyFn(value_type)>::type keytype;
        typedef typename std::result_of<ValueFn(value_type)>::type valtype;
        unordered_multimap<keytype, valtype> mymap;
        std::for_each(begin(), end(), [&mymap, &fnk, &fnv](value_type item) {
            mymap.insert(make_pair(fnk(item), fnv(item)));
        });
        return mymap;
    };


private:
    R m_linqrange;
};

//简化Range的声明
template<
        template<typename T>
        class IteratorRange,
        typename R>
using Range = IteratorRange<decltype((declval<R>()).begin())>;

template<typename R>
using iterator_rang = Range<boost::iterator_range, R>;

//简化定义
template<typename R>
LinqCpp<iterator_rang<R>> from(R &&range) {
    return LinqCpp<iterator_rang<R>>(iterator_rang<R>(range));
}

int main() {
    vector<int> v = {1, 87, 4, 5, 4};
    auto range = make_iterator_range(v);
    //auto r = from(std::move(v))
    //.
    //    select([](int i) {return i+2;}).
    //    where([](int i){return i > 2;}).
    //    max();
    //;
    //cout<<"count="<<r<<endl;

    boost::iterator_range<decltype(v.begin())> ra(range);
    LinqCpp<boost::iterator_range<decltype(v.begin())>> obj(ra);
    cout << obj.where([](int i) { return i > 4; }).max() << endl;

    //测试groupby
    auto m_map = obj.groupby([](int i) {
        return i;
    });
    auto m_map1 = obj.groupby([](int i) {
        return i;
    }, [](int j) {
        return j + 2;
    });
    int i = 0;
    for (auto item : m_map1) {
        cout << item.first << ", " << m_map.bucket_size(i) << endl;
        i++;
    }

    auto reverse = make_iterator_range(v);
    auto res = boost::adaptors::reverse(reverse);
    //boost::iterator_range<decltype(v.begin())> it = res;
    for (auto item = res.begin(); item != res.end(); ++item) {
        cout << *item << " ";
    }
    return 0;
}